/*RuneDev
 *
 */
package org.rsbot.util;

import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * This class provides a File like access to both normal files and jar files.
 * Including support for directories in jar files.
 *
 * @author Qauters
 */
public class GlobalFile extends File {

    private static final long serialVersionUID = 9081385558893463127L;

    public GlobalFile(final File parent, final String child) {
        super(parent, child + (child.endsWith(".jar") ? "!" + File.separator : ""));
    }

    public GlobalFile(final String pathname) {
        super(pathname + (pathname.endsWith(".jar") ? "!" + File.separator : ""));
    }

    public GlobalFile(final String parent, final String child) {
        super(parent, child + (child.endsWith(".jar") ? "!" + File.separator : ""));
    }

    public GlobalFile(final URI uri) {
        super(uri.toString() + (uri.toString().endsWith(".jar") ? "!" + File.separator : ""));
    }

    @Override
    public boolean canRead() {
        if (isJarEntry())
            return getJarEntries() != null;

        return super.canRead();
    }

    @Override
    public boolean canWrite() {
        if (isJarEntry())
            return false;

        return super.canWrite();
    }

    @Override
    public boolean createNewFile() throws IOException {
        if (isJarEntry())
            return false;

        return super.createNewFile();
    }

    @Override
    public boolean delete() {
        if (isJarEntry())
            return false;

        return super.delete();
    }

    @Override
    public void deleteOnExit() {
        if (!isJarEntry()) {
            super.deleteOnExit();
        }
    }

    @Override
    public boolean exists() {
        if (isJarEntry())
            return getJarEntries() != null;

        return super.exists();
    }

    @Override
    public File getAbsoluteFile() {
        return new GlobalFile(getAbsolutePath());
    }

    @Override
    public File getCanonicalFile() throws IOException {
        return new GlobalFile(getCanonicalPath());
    }

    private String getInJarPath() {
        final String path = getPath();
        return path.substring(path.indexOf("jar!") + 4).replace(File.separator, "/");
    }

    private ArrayList<JarEntry> getJarEntries() {
        if (!isJarEntry())
            return null;

        final File f = new File(getJarPath());
        if (!f.exists())
            return null;

        try {
            final ArrayList<JarEntry> jarEntries = new ArrayList<JarEntry>();
            final JarFile jar = new JarFile(f);
            final Enumeration<JarEntry> entries = jar.entries();
            /* Get rel path, without root slash */
            final String relPath = getInJarPath().substring(1);

            while (entries.hasMoreElements()) {
                final JarEntry entry = entries.nextElement();
                final String name = entry.getName();
                if (name.startsWith(relPath)) {
                    jarEntries.add(entry);
                }
            }

            return jarEntries;

        } catch (final Exception e) {
        }

        return null;
    }

    private String getJarPath() {
        final String path = getPath();
        return path.substring(0, path.indexOf("jar!") + 3);
    }

    @Override
    public String getName() {
        return super.getName();
    }

    @Override
    public String getParent() {
        return super.getParent();
    }

    @Override
    public File getParentFile() {
        return super.getParentFile();
    }

    @Override
    public String getPath() {
        String p = super.getPath();
        if (p.contains(".jar!")) {
            p += File.separator;
        }

        return p;
    }

    @Override
    public boolean isDirectory() {
        if (isJarEntry())
            return getPath().endsWith(File.separator) && exists();

        return super.isDirectory();
    }

    @Override
    public boolean isFile() {
        return !isDirectory() && exists();
    }

    @Override
    public boolean isHidden() {
        return super.isHidden();
    }

    public boolean isJarEntry() {
        return getPath().contains(".jar!");
    }

    @Override
    public long lastModified() {
        return super.lastModified();
    }

    @Override
    public long length() {
        return super.length();
    }

    @Override
    public String[] list() {
        if (!isDirectory())
            return null;

        if (isJarEntry()) {
            final ArrayList<String> list = new ArrayList<String>();
            final ArrayList<JarEntry> entries = getJarEntries();
            if (entries == null)
                return null;

            for (final JarEntry entry : entries) {
                /*Get the name and remove the inJarPath */
                final String name = entry.getName().replace(getInJarPath().substring(1), "");
                final String[] split = name.split("/");
                switch (split.length) {
                    case 0:
                        list.add(split[0]);
                        break;

                    case 1:
                        if (!list.contains(split[0])) {
                            list.add(split[0]);
                        }
                        break;
                }
            }

            return list.toArray(new String[0]);
        }

        return super.list();
    }

    @Override
    public File[] listFiles() {
        final String[] ss = list();
        if (ss == null)
            return null;

        final int n = ss.length;
        final File[] fs = new File[n];
        for (int i = 0; i < n; i++) {
            fs[i] = new GlobalFile(this, ss[i]);
        }

        return fs;

    }

    @Override
    public File[] listFiles(final FileFilter filter) {
        final String ss[] = list();
        if (ss == null)
            return null;

        final ArrayList<File> files = new ArrayList<File>();
        for (final String s : ss) {
            final File f = new GlobalFile(this, s);
            if ((filter == null) || filter.accept(f)) {
                files.add(f);
            }
        }

        return files.toArray(new File[files.size()]);

    }

    @Override
    public File[] listFiles(final FilenameFilter filter) {
        final String ss[] = list();
        if (ss == null)
            return null;

        final ArrayList<File> files = new ArrayList<File>();
        for (final String s : ss) {
            if ((filter == null) || filter.accept(this, s)) {
                files.add(new GlobalFile(this, s));
            }
        }

        return files.toArray(new File[files.size()]);

    }

    @Override
    public boolean mkdir() {
        if (isJarEntry())
            return false;

        return super.mkdir();
    }

    @Override
    public boolean mkdirs() {
        if (isJarEntry())
            return false;

        return super.mkdirs();
    }

    @Override
    public boolean renameTo(final File dest) {
        if (isJarEntry())
            return false;

        return super.renameTo(dest);
    }

    @Override
    public boolean setLastModified(final long time) {
        if (isJarEntry())
            return false;

        return super.setLastModified(time);
    }

    @Override
    public boolean setReadOnly() {
        if (isJarEntry())
            return false;

        return super.setReadOnly();
    }

    @Override
    public URI toURI() {
        try {
            /*Generate the uri */
            String uri = (isJarEntry() ? "jar:" : "") + super.toURI().toString();

            /* Add the missing slash for jar directories */
            if (isJarEntry() && isDirectory() && !uri.endsWith("/")) {
                uri += "/";
            }

            return new URI(uri);
        } catch (final Exception e) {
        }

        return super.toURI();
    }
}
